import {celebrate, Joi} from "celebrate";
import {withLock} from "easylock";
import * as express from 'express'
import {InstanceType} from 'typegoose'
import {
  GiftState,
  MailModel,
  MailState,
  MailType,
  PublicMailModel,
  PublicMailRecordModel
} from "../database/models/mail";
import {Player, PlayerModel} from "../database/models/player";
import {InternalError} from "./error";
import {Response, withUser} from "./middlewareType";
import {jwtAuthMiddleware} from "./passort";

const router = express.Router()

export default router

async function getAllMails(player: InstanceType<Player>) {
  const privateMails = await MailModel
    .find({to: player._id})
    .sort({createAt: -1}).lean().exec()

  const publicMails = await PublicMailModel.find().sort({createAt: -1}).lean().exec()

  const publicMailRecords = await PublicMailRecordModel.find({
    player: player._id
  }).lean().exec()

  publicMails.forEach(mail => {
    const rec = publicMailRecords.find(r => r.mail.toString() === mail._id.toString())
    if (!rec) {
      mail.state = MailState.UNREAD
      mail.giftState = GiftState.AVAILABLE
    } else {
      mail.state = rec.state
      mail.giftState = rec.giftState || GiftState.AVAILABLE
    }
  })

  const mails = publicMails
    .filter(m => m.state !== MailState.DELETE)
    .concat(privateMails)
  return mails
}

async function readMail(player: InstanceType<Player>, mailId: string) {
  try {
    await MailModel.update({to: player._id, _id: mailId}, {
      $set: {state: MailState.READ}
    }).exec()

  } catch (e) {
  }
}

async function readPublic(player: InstanceType<Player>, noticeId: string) {

  const m = await PublicMailModel.findById(noticeId)

  if (!m) {
    return
  }

  await PublicMailRecordModel.findOneAndUpdate(
    {player: player._id, mail: noticeId},
    {$set: {state: MailState.READ}}, {upsert: true, setDefaultsOnInsert: true}).exec()

}

router.get('/get/all',
  jwtAuthMiddleware,
  async (req: express.Request & withUser, res: Response) => {
    try {
      const mails = await getAllMails(req.user)
      res.json({ok: true, data: {mails}})

    } catch (e) {
      res.error(Error('MAIL_FETCH_ERROR'))
    }
  })

router.put('/read/allMail',
  jwtAuthMiddleware,
  async (req: express.Request & withUser, res: Response) => {

    await MailModel.update({to: req.user._id}, {
      $set: {state: MailState.READ}
    }, {multi: true}).exec()

    res.json({ok: true})
  })

router.put('/read/mail/:mailId',
  jwtAuthMiddleware,
  celebrate({
    params: Joi.object({
      mailId: Joi.string().required()
    })
  }),
  async (req: express.Request & withUser, res: Response) => {

    await readMail(req.user, req.params.mailId)
    res.json({ok: true})
  })

router.put('/read/public/:publicId',
  jwtAuthMiddleware,
  celebrate({
    params: Joi.object({
      publicId: Joi.string().required()
    })
  }),
  async (req: express.Request & withUser, res: Response) => {
    try {
      await readPublic(req.user, req.params.publicId)
      res.json({ok: true})
    } catch (e) {
      res.error(e)
    }
  })

router.delete('/delete/mail/:mailId',
  jwtAuthMiddleware,
  celebrate({
    params: Joi.object({
      mailId: Joi.string().required()
    })
  }),
  async (req: express.Request & withUser, res) => {
    await MailModel.deleteOne({
      to: req.user._id,
      _id: req.params.mailId
    }).exec()

    res.json({ok: true})
  }
)

router.delete('/delete/public/:publicId',
  jwtAuthMiddleware,
  celebrate({
    params: Joi.object({
      publicId: Joi.string().required()
    })
  }),
  async (req: express.Request & withUser, res) => {

    const pmr = await PublicMailRecordModel.findOneAndUpdate(
      {player: req.user._id, mail: req.params.publicId},
      {$set: {state: MailState.DELETE}}, {upsert: true, new: true}).exec()

    res.json({ok: true})
  }
)

router.put('/requestGift/mail/:mailId',
  jwtAuthMiddleware,
  celebrate({
    params: Joi.object({
      mailId: Joi.string().required()
    })
  }),
  async (req: express.Request & withUser, res: Response) => {
    await withLock(req.user.id, async () => {
      const originalGiftMail =
        await MailModel.findOneAndUpdate({to: req.user.id, _id: req.params.mailId, type: MailType.GIFT}, {
          $set: {giftState: GiftState.REQUESTED, state: MailState.REQUESTED}
        })
      if (!originalGiftMail)
        return res.error(InternalError("NO_SUCH_GIFT_MAIL"))

      if (originalGiftMail.giftState !== GiftState.AVAILABLE)
        return res.error(InternalError("NO_SUCH_GIFT_MAIL"))

      const updatedModel = await PlayerModel.findByIdAndUpdate({_id: req.user._id},
        {$inc: originalGiftMail.gift},
        {new: true, select: {gem: 1, freeCoin: 1, coin: 1, point: 1}, rawResult: true})

      res.json({
        ok: true, data: {
          gift: originalGiftMail.gift
        }
      })
    })
  }
)

router.put('/requestGift/public/:publicId',
  jwtAuthMiddleware,
  celebrate({
    params: Joi.object({
      publicId: Joi.string().required()
    })
  }),
  async (req: express.Request & withUser, res: Response) => {
    await withLock(req.user.id, async () => {
      const publicM =
        await PublicMailModel.findOne({_id: req.params.publicId, type: MailType.NOTICEGIFT})

      if (!publicM)
        return res.error(InternalError("NO_SUCH_GIFT_MAIL"))

      const giftMail =
        await PublicMailRecordModel.findOneAndUpdate({mail: publicM.id, player: req.user.id}, {
          $set: {giftState: GiftState.REQUESTED}
        })
      if (!giftMail)
        return res.error(InternalError("NO_SUCH_GIFT_MAIL"))

      if (giftMail.giftState !== GiftState.AVAILABLE)
        return res.error(InternalError("NO_SUCH_GIFT_MAIL"))

      const updatedModel = await PlayerModel.findByIdAndUpdate({_id: req.user._id},
        {$inc: publicM.gift},
        {new: true, select: {gem: 1, freeCoin: 1, coin: 1, point: 1}, rawResult: true})

      res.json({
        ok: true, data: {
          gift: publicM.gift
        }
      })
    })

  }
)

